급수 계산: if와 while의 활용.

컴퓨터는 반복적인 계산을 하기에 적합하다. 이 장에서는 이런 유형의 문제를 프로그램으로 해결해 보고자 한다. 첫 번째의 예제로 [eqn: pi]값을 급수를 이용해 계산해보자. [eqn: tan(x)]는 [eqn: tan(pi/4) = 1]를 만족하므로 [eqn: tan(x)]의 역함수인 [eqn: arctan(x)]를 이용하면 [eqn: pi = 4 arctan(1)]로 적을 수 있다. [eqn: arctan(x)]의 테일러 전개를 적어보면 아래와 같다.

[eqn: arctan(x)=x- {x ^{3}} over {3} + {x ^{5}} over {5} - {x ^{7}} over {7} + CDOTS]

위의 식에 [eqn: x=1]을 대입해서 얻게 되는 급수 [eqn: 1`- 1 over3 + 1 over5 - 1over7 + 1 over9 + cdots] 에 4를 곱하면 [eqn: pi]의 어림값을 얻을 수 있다. 급수의 n번째 항은 [eqn: (-1)^n over 2n+1]의 꼴임을 이용하자.

N = 10000

sum = 0.0

for n in range(N):

x = (-1)**n/(2*n+1)

sum += x

print(n, 4*sum)

위의 코드에서 a += b는 a = a+b와 같다. 마찬가지로 a *= b는 a = a*b와 같은데, 둘 모두 a += b, 그리고 a *= b의 꼴로 적는 것이 더 효율적이고 더 직관적이다. 위의 프로그램을 실행해보면 원하는 [eqn: pi]값에 수렴하기까지 상당히 많은 항을 더해 나가야 하는 것을 알 수 있어서, for loop이 실행될 때 모든 n값에 대해서 결과를 출력하는 것보다 적당한 간격으로 출력하는 것이 좋다. 예를 들어 n이 100, 200, 300,..처럼 100의 간격으로 결과를 출력하려면 어떻게 할까. n을 100으로 나눈 나머지가 0이 될 때만 출력하려면 아래와 같이 수정하면 된다.

if (n%100 == 0):

print(n,4*sum)

여기서 a%b는 a를 b로 나눈 나머지를 뜻하고, x == y는 x와 y가 같다면 True를, 다르면 False를 주는 논리 연산이다. if (...): 는 괄호안의 논리값이 True일 때만 그 아래의 부분을 실행하게 하는 조건문이다. for loop대신에 파이선의 while을 이용해 같은 계산을 할 수도 있다. while(...)은 괄호 안이 True인 경우에는 계속 loop을 반복하다가 False가 되면 loop을 중단하게 된다. 위의 계산과 정확히 같은 식으로 작동하는 프로그램을 while을 이용해 작성하면 아래와 같다.

N = 10000

sum = 0.0

n = 0

while( n < N ):

x = (-1)**n/(2*n+1)

sum += x

if (n%100 == 0):

print(n, 4*sum)

n += 1

n은 0부터 시작해서 1씩 증가하는데 while( n < N )을 이용했으므로, n이 N보다 작다면 계속 계산을 반복하게 된다. 10,000개의 항에 대해 급수를 계산한 결과를 보면 3.1417정도의 값이다. 만약 얻고자 하는 값이 소숫점 아래 2자리 정도까지였다면 10,000개의 항에 대한 덧셈을 할 필요까지는 없다고 할 수 있다. 급수를 계속 계산하는 도중에 더 이상 값에 큰 변화가 없게 되면 계산을 중간에 중단하도록 프로그램을 작성할 수 있다. 예를 들어, 현재의 급수값에 더해지는 항 하나의 값이 [eqn: 10 ^{-4}]보다 작으면 loop이 중단되게 하려면 아래처럼 하면 된다.

N = 10000

eps = 1E-4

sum = 0.0

n = 0

while( n < N ):

x = (-1)**n/(2*n+1)

sum += x

if ( abs(x) < eps ): break

if (n%100 == 0):

print(n, 4*sum)

n += 1

print(n, abs(x), 4*sum, np.pi)

많은 컴퓨터 프로그램 언어에서 [eqn: 10 ^{-4}]은 “1E-4”로 적는다는 것도 기억하자. 새로 추가된 줄 “if ( abs(x) < eps ): break”을 보면, 만약 x의 절대값(abs)이 미리 정해놓은 eps의 값보다 작으면 while loop이 중단(break)되도록 되어 있다. 이처럼 조건이 성립할 때 수행되는 코드가 길이가 짧을 때에는 줄을 바꾸지 않고 콜론(:)뒤에 적는 것도 가능하다(if 뿐 아니라, for나 while문도 마찬가지다). 위 프로그램의 while loop은 n이 N에 도달해서 종료될 수도 있고, 혹은 x의 절댓값이 충분히 작아져서 중단될 수도 있다. 프로그램이 실행된 결과를 보면 x의 절댓값이 정해놓은 기준값보다 더 작아져서 중단되었음을 알 수 있다.

== 예제 1 ==

위의 프로그램을 이용해서 n의 함수로 [eqn: pi]의 어림값을 구해 그래프로 그리시오. 그래프에는 np.pi에 들어있는 [eqn: pi]값을 수평선으로 함께 표시하시오.

== 예제 2 ==

Riemann의 zeta함수는 물리학에서 자주 등장한다. [eqn: zeta (2) = 1 + 1 over 2^2 + 1over3^2 + 1over4^2 + cdots]의 어림값을 구해 위의 예제와 마찬가지로 n의 함수로 그래프로 그리시오.

== 과제 1 ==

연이율 100%인 정기예금 가입자가 1년 뒤에 받게 되는 돈은 원금의 2배다. 같은 연이율로 6개월 동안 예금하고 6개월 후 원리금을 찾으면 원금의 (1+1/2)배를 받게 되고, 이를 다시 정기예금에 가입하면 일년 뒤에 받게 되는 돈은 처음 원금의 [eqn: (1 + 1/2)^2]배가 된다. 이를 일반화하면 1년이라는 전 기간을 n개의 구간으로 나눈 후 복리로 계산하면 원금의 [eqn: (1 + 1/n)^n]배를 받게 된다. [eqn: n]의 함수로 [eqn: (1 + 1/n)^n]를 구하고, 이를 상수 [eqn: e]의 값과 비교해 그래프로 그리는 프로그램을 작성하시오.

== 파이선 요약 ==

sum += x # 이 식은 sum = sum + x와 같지만, 더 효율적인 코딩 방식이다.

if (n%100 == 0): # “n을 100으로 나눈 나머지가 0이다”가 참(True)이면 아래를 실행한다.

print(n,4*sum)

while(n < N): # “n이 N보다 작다”가 참(True)이면 아래에 이어지는 부분을 계속 반복한다.

break # while이나 for를 이용한 반복계산이 break를 만나면 중단되어 loop아래로 건너뛰어 프로그램 실행이 이어진다.